#!/usr/bin/python
# -*- coding: utf-8 -*-

##
 # Copyright 2009 Caio Souza
 # This file is part of Rpyg.
 #
 # Rpyg is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
 # Rpyg is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with Rpyg. If not, see <http://www.gnu.org/licenses/>.
 ##

'''Módulo do mapa de tiles de uma dungeon.'''

import threading

import pygame

from rpyg.Blitable import Blitable
from rpyg.Map import Map
from rpyg.Screen import Screen
from rpyg.Tile import Tile
from rpyg.TileSet import TileSet
from rpyg.Utils import *

class TileMap (Map, Blitable):
    '''Mapa de tiles onde caminham os personagens.'''

    def __init__ (self, matrix, deltax, deltay):
        '''Cria um novo mapa de tiles.'''

        Map.__init__ (self, matrix)
        Blitable.__init__ (self)

        self.deltax = deltax
        self.deltay = deltay

        self._bottom_surface = pygame.Surface ((self._w * self.deltax, self._h * self.deltay), pygame.SRCALPHA).convert_alpha ()
        self._top_surface = pygame.Surface ((self._w * self.deltax, self._h * self.deltay), pygame.SRCALPHA).convert_alpha ()
        self._bottom_animated = []
        self._top_animated = []

        self._normalizeTiles ()
        self._expandTileSet (self)
        self._createBlocked ()
        self._createSurfaces ()

        self._virtual = (0, 0)
        '''Posição virtual do mapa.'''

        self._lock = threading.RLock ()
        '''Cadeado para sincronia da posição virtual.'''

    def _normalizeTiles (self):
        '''Normaliza a lista de tiles no formato padrão de entrada.'''

        for i in range (len (self._matrix)):
            for j in range (len (self._matrix[0])):
                tile = self._matrix[i][j]
                if isinstance (tile, Tile): self._matrix[i][j] = [tile]

    def _expandTileSet (self, matrix):
        '''Expande os conjuntos de tiles.'''

        for tile in matrix:
            if isinstance (tile, TileSet):
                tileset = tile
                self._expandTileSet (tileset)

                for tile in tileset:
                    tile.i = tile.i + tileset.i
                    tile.j = tile.j + tileset.j
                    matrix._matrix[tile.i][tile.j].append (tile)

    def _createSurfaces (self):
        '''Cria as superfícies do mapa ao contrário para que imagens grandes possam sobrescrever as outras.'''

        matrix = reversed (self._matrix)
        for row in matrix:
            row = reversed (row)
            for tiles in row:
                # TODO: Criar uma lista de tiles dinâmicos
                for tile in tiles:
                    if tile: self.appendTile (tile, tile.i, tile.j)

    def _createBlocked (self):
        '''Cria a matriz de itens bloqueados para movimentação.'''

        self._blocked_matrix = []
        '''Matrix de tiles bloqueados.'''

        for row in self._matrix:
            self._blocked_matrix.append ([])

            for tiles in row:
                self._blocked_matrix[-1].append (list (tiles))

    def updateBottom (self):
        '''Exibe na tela a parte baixa do mapa.'''

        self._lock.acquire ()

        self.blit (self._bottom_surface, (0, 0))

        for tile in self._bottom_animated:
            self.blit (tile.getSurface (), (tile.i * self.deltax, tile.j * self.deltay))

    def updateTop (self):
        '''Exibe na tela a parte alta do mapa.'''

        self.blit (self._top_surface, (0, 0))

        for tile in self._top_animated:
            self.blit (tile.getSurface (), (tile.i * self.deltax, tile.j * self.deltay))

        self._lock.release ()

    def appendTile (self, tile, i, j):
        '''Adiciona o tile ao mapa.'''

        tile.i = i
        tile.j = j

        self._matrix[i][j] = tile

        if tile.getDepth () < 0:
            if len (tile) > 1: self._bottom_animated.append (tile)
            else: self._bottom_surface.blit (tile.getSurface (), (i * self.deltax, j * self.deltay))
        else:
            if len (tile) > 1: self._top_animated.append (tile)
            else: self._top_surface.blit (tile.getSurface (), (i * self.deltax, j * self.deltay))

    def isBlocked (self, i, j, key = None):
        '''Retorna True caso o tile esteja bloqueado.'''

        i, j = incTile (i, j, key)

        if i < 0 or j < 0: return True

        try:
            for tile in self._blocked_matrix[i][j]:
                if isinstance (tile, Tile):
                    if tile.isBlocked (): return True
                else: return True
            return False
        except IndexError: return True

    def setBlocked (self, i, j, object = True):
        '''Bloqueia o tile do mapa com o objeto passado.'''

        self._blocked_matrix[i][j].append (object)

    def setUnblocked (self, i, j, object = True):
        '''Retira o objeto passado da lista de tiles bloqueados.'''

        try: self._blocked_matrix[i][j].remove (object)
        except ValueError: pass

    def executeEvent(self, trigger, i, j):
        '''Executa um evento da lista de eventos.'''

        if i < 0 or j < 0: return
        try: events = self._blocked_matrix[i][j]
        except IndexError: return

        for event in events:
            if hasattr(event, 'executeEvent'): event.executeEvent(trigger)

    def blit (self, image, pos):
        '''Imprime uma imagem no mapa.'''

        with self._lock:
            x = pos[0] + self._virtual[0]
            y = pos[1] + self._virtual[1]

        Screen.blit (image, (x, y))

    def setPlayerPos (self, x, y):
        '''Atualiza a posição do jogador principal no mapa.'''

        x += self.deltax // 2
        y += self.deltay // 2

        sw, sh = Screen.getSize ()
        w, h = self.deltax * self._w, self.deltay * self._h

        if x < sw // 2: vx = 0
        elif x > w - sw // 2: vx = w - sw
        else: vx = x - sw // 2

        if y < sh // 2: vy = 0
        elif y > h - sh // 2: vy = h - sh
        else: vy = y - sh // 2

        with self._lock: self._virtual = (-vx, -vy)
